~ function() {
    function atMe(opts) {
        /**
         *@param {object} options 传入参数
         *@param {Element||#id||className} options.target 目标textarea
         *@param {Array} options.friends 你的好友
         *@param {String} options.prompt 提示符
         */
        var options = {
                target: '',
                friends: [],
                prompt: '@'
            },
            cache = {},
            util = {
                addClass: function(o, targetClass) {
                    if (!util.hasClass(o, targetClass)) {
                        o.className += (' ' + targetClass);
                    }
                },
                removeClass: function(o, targetClass) {
                    var cs;
                    if (util.hasClass(o, targetClass)) {
                        cs = o.className;
                        o.className = cs.replace(targetClass, '').replace(/\s{2,}/g, ' ');
                    }
                },
                hasClass: function(o, targetClass) {
                    var cs = o.className;
                    if (cs.indexOf(targetClass) != -1) {
                        return true;
                    }
                    return false;
                },
                getEle: function(target) {
                    //at least ie8 suggests
                    return document.querySelector(target);
                },
                extend: function(dest, src) {
                    for (var i in src) {
                        dest[i] = src[i];
                    }
                }

            },
            action = {
                addEvent: function(type, ele, func) {
                    if (ele.addEventListener) {
                        ele.addEventListener(type, func);
                    } else if (ele.attachEvent) {
                        ele.attachEvent('on' + type, func);
                    }
                },
                //friendsInfo Array
                buildFriendsList: function(friendsInfo) {
                    var ul = document.createElement('ul'),
                        li = null,
                        a = null,
                        list;
                    li = document.createElement('li');
                    li.innerText = '选择昵称或轻敲空格完成输入';
                    li.className = 'suggest-title';
                    ul.appendChild(li);
                    for (var i in friendsInfo) {
                        a = document.createElement('a');
                        a.innerText = friendsInfo[i];
                        li = document.createElement('li');
                        li.appendChild(a);
                        ul.appendChild(li);
                    }
                    //ie9
                    ul.children[1].className = 'cur';
                    cache.friendsList = document.createElement('div');
                    cache.friendsList.className = 'menu-list';
                    cache.friendsList.appendChild(ul);
                    document.body.appendChild(cache.friendsList);
                    cache.friendsList = util.getEle('.menu-list');
                    list = cache.friendsList.firstChild;
                    //bind
                    action.addEvent('click', cache.friendsList.firstChild, function(e) {
                        // Support IE6-8
                        var target = e.target || e.srcElement,
                            temp = cache.text,
                            ul;
                        if (target.nodeName == 'A') {
                            cache.friendsList.style.display = 'none';
                            temp.PromptT = cache.prompt + target.innerText + ' ';
                            cache.target.value = temp.beforePromptT + temp.PromptT + temp.cursorAfterText;
                            cache.target.setSelectionRange((temp.beforePromptT + temp.PromptT).length, (temp.beforePromptT + temp.PromptT).length);
                            cache.target.focus();
                        }
                    });

                    action.addEvent('mouseover', list, function(e) {
                        var target = e.target || e.srcElement;
                        if (target.nodeName == 'A') {
                            for (var i = 1; i < list.children.length; i++) {
                                util.removeClass(list.children[i], 'cur');
                            }
                            util.addClass(e.target.parentElement, 'cur');
                        }
                    });
                    a = null;
                    li = null;
                    ul = null;
                },
                buildShadow: function() {
                    var sdiv = document.createElement('div'),
                        size = getComputedStyle(cache.target),
                        psize = getComputedStyle(cache.target.parentElement);
                    sdiv.style.cssText = ';' + 'width:' + size.width + ';height:' + size.height + ';' +
                        'margin-left:' + size.marginLeft + ';' +
                        'margin-right:' + size.marginRight + ';' +
                        'margin-top:' + size.marginTop + ';' +
                        'margin-bottom:' + size.marginBottom + ';' +
                        'padding-left:' + size.paddingLeft + ';' +
                        'padding-right:' + size.paddingRight + ';' +
                        'padding-top:' + size.paddingTop + ';' +
                        'padding-bottom:' + size.paddingBottom + ';' +
                        'position:absolute;' +
                        'font-family: "Microsoft YaHei";' +
                        'font-size: 14px;' +
                        'word-wrap: break-word;' +
                        'line-height: 18px;' +
                        'overflow-y: auto;' +
                        'overflow-x: hidden;' +
                        'visibility: hidden;' +
                        'opacity: 0;' +
                        'left:0;' +
                        'top:0;' +
                        'z - index: -1;';
                    sdiv.id = 'atMe-shadow';
                    cache.target.parentElement.appendChild(sdiv);
                    cache.shadow = sdiv;
                    sdiv = null;
                },
                getPos: function() {
                    var prompt = cache.shadow.querySelector('#atMe-prompt');
                    return prompt.getBoundingClientRect();
                },
                showFriendsList: function(pos) {
                    //加17减少偏差
                    cache.friendsList.style.cssText = ';left:' + (pos.left + 17) + 'px;top:' + (pos.top + 22 - cache.target.scrollTop) + 'px;display:block;';
                },
                doWhenKeyUp: function(e) {
                    var temp = cache.text,
                        prompt = cache.prompt,
                        pos, list, i, target;
                    temp.curT = cache.target.value;
                    temp.curTL = cache.target.value.length;
                    //未考虑ie
                    //输入符位置
                    temp.cursorPosition = cache.target.selectionEnd;
                    //输入符之前的文本
                    temp.cursorBeforeText = temp.curT.substr(0, temp.cursorPosition);
                    //输入符之前最近的提示符的位置
                    temp.promptP = temp.cursorBeforeText.lastIndexOf(cache.prompt);
                    //提示符之前的文本
                    temp.beforePromptT = temp.cursorBeforeText.substr(0, temp.promptP);
                    //提示符和输入符之间的文本
                    temp.person = temp.cursorBeforeText.substr(temp.promptP, temp.cursorPosition - temp.promptP);
                    //之后的文本
                    temp.cursorAfterText = temp.curT.substr(temp.cursorPosition);

                    temp.cursorBeforeTextL = temp.cursorBeforeText.length;
                    list = cache.friendsList.firstChild;
                    if (cache.friendsList.style.display == 'block') {
                        if (e.keyCode == 38) {
                            //up
                            cache.target.setSelectionRange(temp.fixedP, temp.fixedP);
                            cache.target.focus();
                            for (i = 1; i < list.children.length; i++) {
                                if (util.hasClass(list.children[i], 'cur')) {
                                    if (i == 1) {
                                        util.removeClass(list.children[1], 'cur');
                                        util.addClass(list.children[list.children.length - 1], 'cur');
                                        break;
                                    } else {
                                        util.removeClass(list.children[i], 'cur');
                                        util.addClass(list.children[i - 1], 'cur');
                                        break;
                                    }
                                }
                            }
                            return false;
                        } else if (e.keyCode == 40) {
                            //down
                            cache.target.setSelectionRange(temp.fixedP, temp.fixedP);
                            cache.target.focus();
                            for (i = 1; i < list.children.length; i++) {
                                if (util.hasClass(list.children[i], 'cur')) {
                                    if (i == list.children.length - 1) {
                                        util.removeClass(list.children[list.children.length - 1], 'cur');
                                        util.addClass(list.children[1], 'cur');
                                        break;
                                    } else {
                                        util.removeClass(list.children[i], 'cur');
                                        util.addClass(list.children[i + 1], 'cur');
                                        break;
                                    }
                                }
                            }
                            return false;
                        } else if (e.keyCode == 13) {
                            //enter
                            cache.target.setSelectionRange(temp.fixedP, temp.fixedP);
                            cache.target.focus();
                            for (i = 1; i < list.children.length; i++) {
                                if (util.hasClass(list.children[i], 'cur')) {
                                    target = list.children[i].firstChild;
                                    break;
                                }
                            }
                            cache.friendsList.style.display = 'none';
                            temp.PromptT = cache.prompt + target.innerText + ' ';
                            cache.target.value = temp.beforePromptT + temp.PromptT + temp.cursorAfterText;
                            cache.target.setSelectionRange((temp.beforePromptT + temp.PromptT).length, (temp.beforePromptT + temp.PromptT).length);
                            cache.target.focus();
                            return false;
                        } else {
                            cache.friendsList.style.display = 'none';
                        }

                    }
                    //输入@
                    if (temp.promptP != -1 && temp.person.indexOf(' ') == -1) {
                        temp.beforePromptT = temp.cursorBeforeText.substr(0, temp.promptP);
                        cache.shadow.innerHTML = temp.beforePromptT + '<span id="atMe-prompt">@</span>';
                        temp.fixedP = temp.cursorPosition;
                        action.showFriendsList(action.getPos());
                    }
                }
            };
        this.init = function() {

            util.extend(options, opts);
            if (!options.target) {
                throw Error('at least one target !');
            }
            cache.prompt = options.prompt;
            cache.target = typeof options.target === 'string' ?
                util.getEle(options.target) : options.target;
            cache.friends = options.friends;
            cache.friendsList = options.friendsList || util.getEle('.menu-list');
            action.buildFriendsList(cache.friends);
            action.buildShadow();
            cache.text = {};
            action.addEvent('keyup', document, action.doWhenKeyUp);
        };
        this.rebuildFriendsList = function(infos) {
            action.buildFriendsList(infos);
        }
        this.getCache = function() {
            return cache;
        };
        this.init();
    }
    window.atMe = atMe;
}();